import tkinter as tk
from tkinter import ttk, messagebox, font
import time
from datetime import datetime
from config_manager import ConfigManager
from device_manager import DeviceManager
from window_manager import WindowManager
from ffmpeg_pusher import FFmpegPusher
from preview_manager import PreviewManager
import win32gui
import win32api
import win32con
import threading
import numpy as np

class UIManager:
    def __init__(self, root):
        self.root = root
        self.config_manager = ConfigManager()
        self.device_manager = DeviceManager(self.config_manager.get_config("ffmpeg_path"))
        self.window_manager = WindowManager()
        self.ffmpeg_pusher = FFmpegPusher(
            self.config_manager.get_config("ffmpeg_path"),
            self.config_manager.get_config("rtmp_url"),
            self.log
        )
        self.preview_manager = PreviewManager(self.log, self.config_manager.get_config("ffmpeg_path"))
        self.mini_window = None
        
        # 绑定窗口最小化事件
        self.root.bind("<Unmap>", self._on_minimize)
        
        # 初始化界面
        self._init_ui()
        
    def _init_ui(self):
        """初始化用户界面"""
        # 设置中文字体支持
        self.setup_fonts()
        
        # 主窗口设置
        self.root.title("PC推流工具")
        self.root.geometry("900x700")
        self.root.minsize(800, 600)
        
        # 设置主题样式
        self.style = ttk.Style()
        self.setup_theme()
        
        # 创建选项卡控件
        self.tab_control = ttk.Notebook(self.root)
        
        # 设备推流选项卡
        self.device_tab = ttk.Frame(self.tab_control)
        self.tab_control.add(self.device_tab, text="摄像头推流")
        
        # 窗口推流选项卡
        self.window_tab = ttk.Frame(self.tab_control)
        self.tab_control.add(self.window_tab, text="应用窗口推流")
        
        # 录屏推流选项卡
        self.screen_tab = ttk.Frame(self.tab_control)
        self.tab_control.add(self.screen_tab, text="录屏推流")
        
        # 本地文件推流选项卡
        self.file_tab = ttk.Frame(self.tab_control)
        self.tab_control.add(self.file_tab, text="本地文件推流")
        
        self.tab_control.pack(expand=1, fill="both", padx=10, pady=10)
        
        # 创建各选项卡界面
        self._create_device_tab()
        self._create_window_tab()
        self._create_screen_tab()
        self._create_file_tab()
        
        # 创建底部状态栏
        self.status_var = tk.StringVar()
        self.status_var.set("就绪")
        status_bar = ttk.Label(self.root, textvariable=self.status_var, relief=tk.SUNKEN, anchor=tk.W, padding=(5, 2))
        status_bar.pack(side=tk.BOTTOM, fill=tk.X)
        
        # 创建菜单
        self._create_menu()
        
        # 音频电平显示变量
        self.audio_level = 0
        self.audio_monitoring = False
        
        # 初始获取设备列表
        self.list_devices()
        
        # 绑定关闭事件
        self.root.protocol("WM_DELETE_WINDOW", self.on_closing)
    
    def setup_fonts(self):
        """设置中文字体支持"""
        default_font = tk.font.nametofont("TkDefaultFont")
        default_font.configure(family="微软雅黑", size=10)
        text_font = tk.font.nametofont("TkTextFont")
        text_font.configure(family="微软雅黑", size=10)
        fixed_font = tk.font.nametofont("TkFixedFont")
        fixed_font.configure(family="Consolas", size=10)
    
    def setup_theme(self):
        """设置主题样式"""
        # 使用ttkbootstrap风格的配色方案
        self.style.configure(
            "TNotebook",
            tabposition="n",
            padding=(5, 2)
        )
        self.style.configure(
            "TNotebook.Tab",
            padding=(15, 5),
            font=(
"微软雅黑", 10),
            focuscolor="#4a7a8c",
            bordercolor="#cccccc"
        )
        self.style.map(
            "TNotebook.Tab",
            background=[("selected", "#ffffff"), ("!selected", "#f0f0f0")],
            foreground=[("selected", "#000000"), ("!selected", "#666666")],
            relief=[("selected", "flat"), ("!selected", "groove")]
        )
        # 按钮样式
        self.style.configure(
            "TButton",
            padding=(10, 3),
            font=(
"微软雅黑", 10)
        )
        self.style.map(
            "TButton",
            background=[("active", "#e0e0e0")],
            foreground=[("disabled", "#a0a0a0")]
        )
        # 标签样式
        self.style.configure(
            "TLabel",
            font=(
"微软雅黑", 10)
        )
        # 组合框样式
        self.style.configure(
            "TCombobox",
            font=(
"微软雅黑", 10),
            padding=(5, 2)
        )
    
    def _create_menu(self):
        """创建菜单"""
        menubar = tk.Menu(self.root, font=("微软雅黑", 9))
        
        # 文件菜单
        file_menu = tk.Menu(menubar, tearoff=0, font=("微软雅黑", 9))
        file_menu.add_command(label="配置", command=self.open_config_dialog, accelerator="Ctrl+S")
        file_menu.add_separator()
        file_menu.add_command(label="退出", command=self.on_closing, accelerator="Alt+F4")
        menubar.add_cascade(label="文件", menu=file_menu)
        
        # 工具菜单
        tools_menu = tk.Menu(menubar, tearoff=0, font=("微软雅黑", 9))
        tools_menu.add_command(label="刷新设备列表", command=self.list_devices, accelerator="F5")
        tools_menu.add_command(label="刷新窗口列表", command=self.list_windows)
        menubar.add_cascade(label="工具", menu=tools_menu)
        
        # 帮助菜单
        help_menu = tk.Menu(menubar, tearoff=0, font=("微软雅黑", 9))
        help_menu.add_command(label="使用帮助", command=self.show_help)
        help_menu.add_separator()
        help_menu.add_command(label="关于", command=self.show_about)
        menubar.add_cascade(label="帮助", menu=help_menu)
        
        self.root.config(menu=menubar)
        
        # 绑定快捷键
        self.root.bind("<F5>", lambda event: self.list_devices())
        self.root.bind("<Control-s>", lambda event: self.open_config_dialog())
    
    def _create_device_tab(self):
        """创建设备推流界面"""
        # 主要内容框架
        main_frame = ttk.Frame(self.device_tab)
        main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        
        # 设备选择区域
        device_frame = ttk.LabelFrame(main_frame, text="设备选择")
        device_frame.pack(fill=tk.X, pady=(0, 15))
        
        # 视频设备选择
        ttk.Label(device_frame, text="视频设备:", width=15).grid(row=0, column=0, padx=10, pady=10, sticky=tk.W)
        self.video_device_var = tk.StringVar()
        self.video_device_combo = ttk.Combobox(device_frame, textvariable=self.video_device_var, width=40)
        self.video_device_combo.grid(row=0, column=1, padx=10, pady=10, sticky=tk.W)
        
        # 刷新设备按钮
        ttk.Button(device_frame, text="刷新设备", command=self.list_devices).grid(row=0, column=2, padx=10, pady=10)
        
        # 音频设备选择和音频电平显示
        ttk.Label(device_frame, text="音频设备:", width=15).grid(row=1, column=0, padx=10, pady=10, sticky=tk.W)
        self.audio_device_var = tk.StringVar()
        self.audio_device_combo = ttk.Combobox(device_frame, textvariable=self.audio_device_var, width=40)
        self.audio_device_combo.grid(row=1, column=1, padx=10, pady=10, sticky=tk.W)
        
        # 音频电平指示器
        self.audio_level_frame = ttk.Frame(device_frame, width=100, height=20, relief=tk.SUNKEN)
        self.audio_level_frame.grid(row=1, column=2, padx=10, pady=10, sticky=tk.W)
        # 修改背景色为浅灰色，并添加提示文字
        self.audio_level_canvas = tk.Canvas(self.audio_level_frame, width=80, height=20, bg="#e0e0e0", highlightthickness=0)
        self.audio_level_canvas.pack(fill=tk.BOTH, expand=True)
        # 添加提示文字
        self.audio_level_text = self.audio_level_canvas.create_text(40, 10, text="音频电平", font=('微软雅黑', 8), fill="#666666")
        # 音频电平条
        self.audio_level_bar = self.audio_level_canvas.create_rectangle(0, 0, 0, 20, fill="#4CAF50")
        # 默认隐藏电平条
        self.audio_level_canvas.itemconfigure(self.audio_level_bar, state="hidden")
        
        # 操作按钮区域
        button_frame = ttk.Frame(main_frame)
        button_frame.pack(fill=tk.X, pady=(0, 15))
        
        self.device_preview_button = ttk.Button(button_frame, text="开始预览", command=self.start_device_preview, width=15)
        self.device_preview_button.pack(side=tk.LEFT, padx=10, pady=10)
        
        self.device_push_button = ttk.Button(button_frame, text="开始推流", command=self.start_device_push, width=15)
        self.device_push_button.pack(side=tk.LEFT, padx=10, pady=10)
        
        # 日志区域
        log_frame = ttk.LabelFrame(main_frame, text="日志")
        log_frame.pack(fill=tk.BOTH, expand=True)
        
        # 创建日志显示的框架
        log_display_frame = ttk.Frame(log_frame)
        log_display_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        
        self.log_text = tk.Text(log_display_frame, height=15, width=80, wrap=tk.WORD, font=('Consolas', 10))
        self.log_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        scrollbar = ttk.Scrollbar(log_display_frame, command=self.log_text.yview)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        self.log_text.config(yscrollcommand=scrollbar.set)
        
        # 设置网格权重
        device_frame.grid_columnconfigure(1, weight=1)
    
    def _create_window_tab(self):
        """创建窗口推流界面"""
        # 主要内容框架
        main_frame = ttk.Frame(self.window_tab)
        main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        
        # 窗口列表区域
        window_frame = ttk.LabelFrame(main_frame, text="窗口选择")
        window_frame.pack(fill=tk.BOTH, expand=True, pady=(0, 15))
        
        # 刷新窗口列表按钮
        ttk.Button(window_frame, text="刷新窗口列表", command=self.list_windows).pack(side=tk.TOP, anchor=tk.W, padx=10, pady=10)
        
        # 窗口列表显示
        list_frame = ttk.Frame(window_frame)
        list_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=(0, 10))
        
        ttk.Label(list_frame, text="可用窗口:").pack(side=tk.TOP, anchor=tk.W, pady=(0, 5))
        self.window_listbox = tk.Listbox(list_frame, width=80, height=15, font=('微软雅黑', 10))
        self.window_listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        scrollbar = ttk.Scrollbar(list_frame, command=self.window_listbox.yview)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        self.window_listbox.config(yscrollcommand=scrollbar.set)
        
        # 音频设备选择区域
        audio_frame = ttk.LabelFrame(main_frame, text="音频设置")
        audio_frame.pack(fill=tk.X, pady=(0, 15))
        
        ttk.Label(audio_frame, text="音频设备:", width=15).grid(row=0, column=0, padx=10, pady=10, sticky=tk.W)
        self.window_audio_var = tk.StringVar()
        self.window_audio_combo = ttk.Combobox(audio_frame, textvariable=self.window_audio_var, width=40)
        self.window_audio_combo.grid(row=0, column=1, padx=10, pady=10, sticky=tk.W)
        
        # 操作按钮区域
        button_frame = ttk.Frame(main_frame)
        button_frame.pack(fill=tk.X)
        
        self.window_preview_button = ttk.Button(button_frame, text="开始预览", command=self.start_window_preview, width=15)
        self.window_preview_button.pack(side=tk.LEFT, padx=10, pady=10)
        
        self.window_push_button = ttk.Button(button_frame, text="开始推流", command=self.start_window_push, width=15)
        self.window_push_button.pack(side=tk.LEFT, padx=10, pady=10)
        
        # 设置网格权重
        audio_frame.grid_columnconfigure(1, weight=1)
    
    def _create_file_tab(self):
        """创建本地文件推流界面"""
        # 主要内容框架
        main_frame = ttk.Frame(self.file_tab)
        main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        
        # 文件选择区域
        file_frame = ttk.LabelFrame(main_frame, text="文件选择")
        file_frame.pack(fill=tk.X, pady=(0, 15))
        
        file_content_frame = ttk.Frame(file_frame)
        file_content_frame.pack(fill=tk.X, padx=20, pady=20)
        
        self.file_path_var = tk.StringVar(value="")
        ttk.Entry(file_content_frame, textvariable=self.file_path_var, width=60).grid(row=0, column=0, padx=10, pady=10, sticky=tk.W)
        ttk.Button(file_content_frame, text="浏览文件", command=self.browse_file, width=12).grid(row=0, column=1, padx=10, pady=10, sticky=tk.W)
        
        # 文件信息区域
        info_frame = ttk.LabelFrame(main_frame, text="文件信息")
        info_frame.pack(fill=tk.X, pady=(0, 15))
        
        info_content_frame = ttk.Frame(info_frame)
        info_content_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=20)
        
        self.file_info_text = tk.Text(info_content_frame, width=80, height=10, wrap=tk.WORD, font=('微软雅黑', 10))
        self.file_info_text.pack(fill=tk.BOTH, expand=True)
        self.file_info_text.config(state=tk.DISABLED)  # 初始为只读
        
        # 推流设置区域
        push_frame = ttk.LabelFrame(main_frame, text="推流设置")
        push_frame.pack(fill=tk.X, pady=(0, 15))
        
        push_content_frame = ttk.Frame(push_frame)
        push_content_frame.pack(fill=tk.X, padx=20, pady=20)
        
        # 视频比特率设置
        ttk.Label(push_content_frame, text="视频比特率:", width=15).grid(row=0, column=0, padx=10, pady=10, sticky=tk.W)
        self.video_bitrate_var = tk.StringVar(value="2000k")
        ttk.Entry(push_content_frame, textvariable=self.video_bitrate_var, width=10).grid(row=0, column=1, padx=10, pady=10, sticky=tk.W)
        
        # 音频比特率设置
        ttk.Label(push_content_frame, text="音频比特率:", width=15).grid(row=1, column=0, padx=10, pady=10, sticky=tk.W)
        self.audio_bitrate_var = tk.StringVar(value="128k")
        ttk.Entry(push_content_frame, textvariable=self.audio_bitrate_var, width=10).grid(row=1, column=1, padx=10, pady=10, sticky=tk.W)
        
        # 操作按钮区域
        button_frame = ttk.Frame(main_frame)
        button_frame.pack(fill=tk.X)
        
        self.file_preview_button = ttk.Button(button_frame, text="开始预览", command=self.preview_file, width=15)
        self.file_preview_button.pack(side=tk.LEFT, padx=10, pady=15)
        
        self.file_push_button = ttk.Button(button_frame, text="开始推流", command=self.start_file_push, width=15)
        self.file_push_button.pack(side=tk.LEFT, padx=10, pady=15)
        self.file_push_button.config(state=tk.DISABLED)  # 初始禁用
        
        self.file_stop_button = ttk.Button(button_frame, text="停止推流", command=self.stop_file_push, width=15)
        self.file_stop_button.pack(side=tk.LEFT, padx=10, pady=15)
        self.file_stop_button.config(state=tk.DISABLED)  # 初始禁用
        
    def _create_screen_tab(self):
        """创建录屏推流界面，支持三种录制模式"""
        # 主要内容框架
        main_frame = ttk.Frame(self.screen_tab)
        main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        
        # 录制模式选择
        mode_frame = ttk.LabelFrame(main_frame, text="录制模式")
        mode_frame.pack(fill=tk.X, pady=(0, 15))
        
        self.record_mode_var = tk.StringVar(value="fullscreen")
        
        # 创建一个居中的框架来放置单选按钮
        radiobutton_frame = ttk.Frame(mode_frame)
        radiobutton_frame.pack(pady=10)
        
        ttk.Radiobutton(radiobutton_frame, text="全屏录制", variable=self.record_mode_var, value="fullscreen", 
                       command=self._update_record_mode_ui, width=15).pack(side=tk.LEFT, padx=15, pady=5)
        ttk.Radiobutton(radiobutton_frame, text="区域录制", variable=self.record_mode_var, value="region", 
                       command=self._update_record_mode_ui, width=15).pack(side=tk.LEFT, padx=15, pady=5)
        ttk.Radiobutton(radiobutton_frame, text="应用录制", variable=self.record_mode_var, value="application", 
                       command=self._update_record_mode_ui, width=15).pack(side=tk.LEFT, padx=15, pady=5)
        
        # 录制模式设置区域
        self.options_notebook = ttk.Notebook(main_frame)
        self.options_notebook.pack(fill=tk.BOTH, expand=True, pady=(0, 15))
        
        # 全屏录制选项
        self.fullscreen_frame = ttk.Frame(self.options_notebook)
        self.options_notebook.add(self.fullscreen_frame, text="全屏设置")
        
        fullscreen_content_frame = ttk.Frame(self.fullscreen_frame)
        fullscreen_content_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=20)
        
        ttk.Label(fullscreen_content_frame, text="显示器索引:", width=15).grid(row=0, column=0, padx=10, pady=20, sticky=tk.W)
        self.screen_index_var = tk.StringVar(value="0")
        ttk.Entry(fullscreen_content_frame, textvariable=self.screen_index_var, width=10).grid(row=0, column=1, padx=10, pady=20, sticky=tk.W)
        ttk.Label(fullscreen_content_frame, text="(默认0，多显示器可设置1, 2...)").grid(row=0, column=2, padx=10, pady=20, sticky=tk.W)
        
        # 区域录制选项
        self.region_frame = ttk.Frame(self.options_notebook)
        self.options_notebook.add(self.region_frame, text="区域设置")
        
        region_content_frame = ttk.Frame(self.region_frame)
        region_content_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=20)
        
        region_button_frame = ttk.Frame(region_content_frame)
        region_button_frame.pack(side=tk.TOP, pady=20)
        
        ttk.Button(region_button_frame, text="选择录制区域", command=self._select_region, width=20).pack(pady=10)
        
        self.region_info_var = tk.StringVar(value="未选择区域")
        ttk.Label(region_content_frame, textvariable=self.region_info_var, font=("微软雅黑", 11)).pack(side=tk.TOP, pady=10)
        self.selected_region = None
        
        # 应用录制选项
        self.application_frame = ttk.Frame(self.options_notebook)
        self.options_notebook.add(self.application_frame, text="应用设置")
        
        app_content_frame = ttk.Frame(self.application_frame)
        app_content_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=20)
        
        ttk.Button(app_content_frame, text="刷新应用窗口", command=self._list_app_windows).pack(side=tk.TOP, anchor=tk.W, pady=10)
        
        ttk.Label(app_content_frame, text="应用窗口:").pack(side=tk.TOP, anchor=tk.W, pady=(10, 5))
        
        app_list_frame = ttk.Frame(app_content_frame)
        app_list_frame.pack(fill=tk.BOTH, expand=True)
        
        self.app_window_listbox = tk.Listbox(app_list_frame, width=80, height=10, font=('微软雅黑', 10))
        self.app_window_listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        scrollbar = ttk.Scrollbar(app_list_frame, command=self.app_window_listbox.yview)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        self.app_window_listbox.config(yscrollcommand=scrollbar.set)
        
        # 通用设置
        common_frame = ttk.LabelFrame(main_frame, text="通用设置")
        common_frame.pack(fill=tk.X, pady=(0, 15))
        
        # 帧率设置
        ttk.Label(common_frame, text="帧率:", width=15).grid(row=0, column=0, padx=10, pady=10, sticky=tk.W)
        self.fps_var = tk.StringVar(value="30")
        fps_frame = ttk.Frame(common_frame)
        fps_frame.grid(row=0, column=1, padx=10, pady=10, sticky=tk.W)
        
        for fps in ["30", "24", "25", "60"]:
            ttk.Radiobutton(fps_frame, text=fps, variable=self.fps_var, value=fps).pack(side=tk.LEFT, padx=10)
        
        # 音频设备选择
        ttk.Label(common_frame, text="音频设备:", width=15).grid(row=1, column=0, padx=10, pady=10, sticky=tk.W)
        self.screen_audio_var = tk.StringVar()
        self.screen_audio_combo = ttk.Combobox(common_frame, textvariable=self.screen_audio_var, width=40)
        self.screen_audio_combo.grid(row=1, column=1, padx=10, pady=10, sticky=tk.W)
        
        # 操作按钮区域
        button_frame = ttk.Frame(main_frame)
        button_frame.pack(fill=tk.X)
        
        self.screen_preview_button = ttk.Button(button_frame, text="开始预览", command=self.start_screen_preview, width=15)
        self.screen_preview_button.pack(side=tk.LEFT, padx=10, pady=15)
        
        self.screen_push_button = ttk.Button(button_frame, text="开始推流", command=self.start_screen_push, width=15)
        self.screen_push_button.pack(side=tk.LEFT, padx=10, pady=15)
        
        # 设置网格权重
        common_frame.grid_columnconfigure(1, weight=1)
        fullscreen_content_frame.grid_columnconfigure(1, weight=1)
    
    def _update_record_mode_ui(self):
        """根据选择的录制模式更新UI显示"""
        mode = self.record_mode_var.get()
        
        # 根据模式选择对应的选项卡
        if mode == "fullscreen":
            self.options_notebook.select(self.fullscreen_frame)
        elif mode == "region":
            self.options_notebook.select(self.region_frame)
        elif mode == "application":
            self.options_notebook.select(self.application_frame)
            # 自动刷新应用窗口列表
            self._list_app_windows()
    
    def log(self, message):
        """添加日志信息"""
        timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        log_message = f"[{timestamp}] {message}"
        self.log_text.insert(tk.END, log_message + "\n")
        self.log_text.see(tk.END)
        print(log_message)
    
    def list_devices(self):
        """列出音视频设备"""
        self.log("正在获取设备列表...")
        self.device_manager.update_ffmpeg_path(self.config_manager.get_config("ffmpeg_path"))
        devices = self.device_manager.list_devices()
        
        # 更新所有选项卡的音频设备下拉框
        audio_devices = devices["audio"]
        
        # 初始化麦克风设备变量，避免后续使用时未定义错误
        mic_device = self.device_manager.get_microphone_device()
        
        # 设备推流选项卡
        self.audio_device_combo['values'] = audio_devices
        # 尝试自动选择麦克风设备
        if self.config_manager.get_config("audio_device") and self.config_manager.get_config("audio_device") in audio_devices:
            self.audio_device_var.set(self.config_manager.get_config("audio_device"))
        else:
            if mic_device:
                self.audio_device_var.set(mic_device)
            elif audio_devices:
                self.audio_device_var.set(audio_devices[0])
        
        # 窗口推流选项卡
        self.window_audio_combo['values'] = audio_devices
        if mic_device:
            self.window_audio_var.set(mic_device)
        elif audio_devices:
            self.window_audio_var.set(audio_devices[0])
        
        # 录屏推流选项卡
        self.screen_audio_combo['values'] = audio_devices
        if mic_device:
            self.screen_audio_var.set(mic_device)
        elif audio_devices:
            self.screen_audio_combo.set(audio_devices[0])
        
        # 更新视频设备下拉框
        video_devices = devices["video"]
        self.video_device_combo['values'] = video_devices
        if self.config_manager.get_config("video_device") and self.config_manager.get_config("video_device") in video_devices:
            self.video_device_var.set(self.config_manager.get_config("video_device"))
        elif video_devices:
            self.video_device_var.set(video_devices[0])
        
        self.log(f"已获取设备列表: 视频设备 {len(video_devices)} 个, 音频设备 {len(audio_devices)} 个")
        # 显示找到的设备
        if video_devices:
            self.log("视频设备:")
            for device in video_devices:
                self.log(f"  - {device}")
        if audio_devices:
            self.log("音频设备:")
            for device in audio_devices:
                self.log(f"  - {device}")
    
    def list_windows(self):
        """列出当前所有窗口"""
        self.window_listbox.delete(0, tk.END)
        windows = self.window_manager.list_windows()
        
        # 添加到列表
        window_list = self.window_manager.get_window_list_for_display()
        for window in window_list:
            self.window_listbox.insert(tk.END, window)
        
        self.log(f"已获取窗口列表: 共 {len(windows)} 个窗口")
    
    def open_config_dialog(self):
        """打开配置对话框"""
        dialog = tk.Toplevel(self.root)
        dialog.title("配置")
        dialog.geometry("500x300")
        dialog.transient(self.root)
        dialog.grab_set()
        
        # FFmpeg路径设置
        ttk.Label(dialog, text="FFmpeg路径:").grid(row=0, column=0, padx=10, pady=10, sticky=tk.W)
        ffmpeg_path_var = tk.StringVar(value=self.config_manager.get_config("ffmpeg_path"))
        ttk.Entry(dialog, textvariable=ffmpeg_path_var, width=40).grid(row=0, column=1, padx=10, pady=10, sticky=tk.W)
        
        def browse_ffmpeg():
            from tkinter import filedialog
            file_path = filedialog.askopenfilename(title="选择FFmpeg可执行文件", filetypes=[("可执行文件", "*.exe"), ("所有文件", "*")])
            if file_path:
                ffmpeg_path_var.set(file_path)
        
        ttk.Button(dialog, text="浏览...", command=browse_ffmpeg).grid(row=0, column=2, padx=10, pady=10)
        
        # RTMP地址设置
        ttk.Label(dialog, text="RTMP地址:").grid(row=1, column=0, padx=10, pady=10, sticky=tk.W)
        rtmp_url_var = tk.StringVar(value=self.config_manager.get_config("rtmp_url"))
        ttk.Entry(dialog, textvariable=rtmp_url_var, width=40).grid(row=1, column=1, padx=10, pady=10, sticky=tk.W)
        
        # 保存按钮
        def save_config():
            self.config_manager.update_config("ffmpeg_path", ffmpeg_path_var.get())
            self.config_manager.update_config("rtmp_url", rtmp_url_var.get())
            # 更新推流器配置
            self.ffmpeg_pusher.update_config(ffmpeg_path_var.get(), rtmp_url_var.get())
            dialog.destroy()
            messagebox.showinfo("成功", "配置已保存")
        
        ttk.Button(dialog, text="保存", command=save_config).grid(row=2, column=1, padx=10, pady=20, sticky=tk.W)
    
    def start_device_preview(self):
        """开始设备预览"""
        if self.preview_manager.is_preview_active():
            # 停止预览
            self.preview_manager.stop_preview()
            # 停止音频监控
            if hasattr(self, 'audio_monitoring') and self.audio_monitoring:
                self.stop_audio_monitoring()
            # 恢复提示文字显示
            if hasattr(self, 'audio_level_canvas') and hasattr(self, 'audio_level_text'):
                self.audio_level_canvas.itemconfigure(self.audio_level_text, state="normal")
                if hasattr(self, 'audio_level_bar'):
                    self.audio_level_canvas.itemconfigure(self.audio_level_bar, state="hidden")
            self.device_preview_button.config(text="开始预览")
            return
        
        # 获取视频设备
        video_device = self.video_device_var.get()
        if not video_device:
            messagebox.showerror("错误", "请选择视频设备")
            return
        
        # 获取音频设备
        audio_device = self.audio_device_var.get() if self.audio_device_var.get() else None
        
        # 开始预览
        if self.preview_manager.start_device_preview(video_device, audio_device):
            # 启动音频电平监控
            if not hasattr(self, 'audio_monitoring') or not self.audio_monitoring:
                self.start_audio_monitoring()
            self.device_preview_button.config(text="停止预览")
            self.status_var.set(f"预览中: 设备 - {video_device}")
    
    def start_device_push(self):
        """开始设备推流"""
        if self.ffmpeg_pusher.is_streaming():
            self.stop_push()
            self.device_push_button.config(text="开始推流")
            return
        
        # 停止预览
        if self.preview_manager.is_preview_active():
            self.preview_manager.stop_preview()
            self.device_preview_button.config(text="开始预览")
        
        # 获取设备信息
        video_device = self.video_device_var.get()
        audio_device = self.audio_device_var.get()
        
        if not video_device:
            messagebox.showerror("错误", "请选择视频设备")
            return
        
        # 保存当前选择的设备
        self.config_manager.update_config("video_device", video_device)
        self.config_manager.update_config("audio_device", audio_device)
        
        # 开始推流
        if self.ffmpeg_pusher.start_device_push(video_device, audio_device):
            self.device_push_button.config(text="停止推流")
            self.status_var.set("推流中: 设备推流")
    
    def _calculate_preview_size(self, original_width, original_height, max_size=640):
        """计算预览窗口大小，保持原始宽高比"""
        ratio = original_width / original_height
        
        if original_width > original_height:
            # 横屏
            preview_width = min(original_width, max_size)
            preview_height = int(preview_width / ratio)
        else:
            # 竖屏
            preview_height = min(original_height, max_size)
            preview_width = int(preview_height * ratio)
        
        return preview_width, preview_height
    
    def start_window_preview(self):
        """开始窗口预览（使用dshow音频捕获）"""
        if self.preview_manager.is_preview_active():
            self.preview_manager.stop_preview()
            self.window_preview_button.config(text="开始预览")
            return
        
        # 获取选中的窗口
        selection = self.window_listbox.curselection()
        if not selection:
            messagebox.showerror("错误", "请选择一个窗口")
            return
        
        index = selection[0]
        window_info = self.window_manager.get_window_info(index)
        if not window_info:
            messagebox.showerror("错误", "无法获取窗口信息")
            return
        
        hwnd, title = window_info
        
        # 再次尝试将窗口置于前台，确保预览时窗口可见
        self.log(f"准备预览窗口: {title}")
        try:
            import win32gui
            # 先最小化再还原，确保窗口状态正常
            win32gui.ShowWindow(hwnd, 9)  # SW_RESTORE = 9
            # 激活窗口并设置为前台窗口
            win32gui.SetForegroundWindow(hwnd)
            # 短暂延迟确保窗口已置于前台
            import time
            time.sleep(0.3)
        except Exception as fw_error:
            self.log(f"将窗口 '{title}' 置于前台失败: {str(fw_error)}")
        
        # 获取音频设备
        audio_device = self.window_audio_var.get()
        
        # 开始预览 - 使用dshow方式
        preview_width, preview_height = 640, 480  # 默认预览尺寸
        if self.preview_manager.start_window_preview_dshow(title, preview_width, preview_height, audio_device=audio_device):
            self.window_preview_button.config(text="停止预览")
            self.status_var.set(f"预览中: 窗口(dshow) - {title}")
    
    def start_window_push(self):
        """开始窗口推流（使用dshow音频捕获）"""
        if self.ffmpeg_pusher.is_streaming():
            self.stop_push()
            self.window_push_button.config(text="开始推流")
            return
        
        # 停止预览
        if self.preview_manager.is_preview_active():
            self.preview_manager.stop_preview()
            self.window_preview_button.config(text="开始预览")
        
        # 获取选中的窗口
        selection = self.window_listbox.curselection()
        if not selection:
            messagebox.showerror("错误", "请选择一个窗口")
            return
        
        index = selection[0]
        window_info = self.window_manager.get_window_info(index)
        if not window_info:
            messagebox.showerror("错误", "无法获取窗口信息")
            return
        
        hwnd, title = window_info
        
        # 再次尝试将窗口置于前台，确保推流时窗口可见
        self.log(f"准备推流窗口: {title}")
        try:
            import win32gui
            # 先最小化再还原，确保窗口状态正常
            win32gui.ShowWindow(hwnd, 9)  # SW_RESTORE = 9
            # 激活窗口并设置为前台窗口
            win32gui.SetForegroundWindow(hwnd)
            # 短暂延迟确保窗口已置于前台
            import time
            time.sleep(0.3)
        except Exception as fw_error:
            self.log(f"将窗口 '{title}' 置于前台失败: {str(fw_error)}")
        
        # 获取音频设备
        audio_device = self.window_audio_var.get()
        
        # 开始推流 - 使用dshow方式
        if self.ffmpeg_pusher.start_window_push_dshow(title, audio_device):
            self.window_push_button.config(text="停止推流")
            self.status_var.set(f"推流中: 窗口推流(dshow) - {title}")
    
    def _update_record_mode_ui(self):
        """根据选择的录制模式更新UI显示"""
        mode = self.record_mode_var.get()
        
        # 隐藏所有模式特定的框架
        self.fullscreen_frame.grid_remove()
        self.region_frame.grid_remove()
        self.application_frame.grid_remove()
        
        # 显示选中模式的框架
        if mode == "fullscreen":
            self.fullscreen_frame.grid(row=1, column=0, columnspan=3, padx=10, pady=10, sticky=tk.EW)
        elif mode == "region":
            self.region_frame.grid(row=1, column=0, columnspan=3, padx=10, pady=10, sticky=tk.EW)
        elif mode == "application":
            self.application_frame.grid(row=1, column=0, columnspan=3, padx=10, pady=10, sticky=tk.EW)
            # 自动刷新应用窗口列表
            self._list_app_windows()
    
    def _list_app_windows(self):
        """列出应用窗口"""
        self.app_window_listbox.delete(0, tk.END)
        windows = self.window_manager.list_windows()
        
        # 添加到列表
        window_list = self.window_manager.get_window_list_for_display()
        for window in window_list:
            self.app_window_listbox.insert(tk.END, window)
        
        self.log(f"已获取应用窗口列表: 共 {len(windows)} 个窗口")
    
    def _select_region(self):
        """选择录制区域"""
        self.root.iconify()  # 最小化主窗口
        
        # 创建一个全屏的半透明窗口用于区域选择
        overlay = tk.Toplevel(self.root)
        overlay.attributes("-fullscreen", True)
        overlay.attributes("-alpha", 0.3)  # 半透明
        overlay.attributes("-topmost", True)
        overlay.configure(bg="black")
        
        # 添加提示文字
        instructions = tk.Label(overlay, text="按住鼠标左键拖动选择区域，按ESC键取消", 
                               bg="#333", fg="white", font=("Arial", 12))
        instructions.place(relx=0.5, rely=0.05, anchor="center")
        
        # 创建一个画布用于绘制选择框
        canvas = tk.Canvas(overlay, cursor="crosshair", bg="")
        canvas.pack(fill=tk.BOTH, expand=True)
        
        # 记录选择的起始点和结束点
        start_x = start_y = 0
        rect_id = None
        selected = False

        def on_mouse_down(event):
            nonlocal start_x, start_y, rect_id
            start_x, start_y = event.x, event.y
            if rect_id:
                canvas.delete(rect_id)
            rect_id = canvas.create_rectangle(start_x, start_y, start_x, start_y, outline="red", width=2)
        
        def on_mouse_move(event):
            nonlocal rect_id
            if rect_id:
                canvas.delete(rect_id)
            rect_id = canvas.create_rectangle(start_x, start_y, event.x, event.y, outline="red", width=2, fill="")

        def on_mouse_up(event):
            nonlocal rect_id, selected
            end_x, end_y = event.x, event.y
            
            # 计算选择区域
            left = min(start_x, end_x)
            top = min(start_y, end_y)
            width = abs(end_x - start_x)
            height = abs(end_y - start_y)
            
            # 确保选择了有效的区域
            if width > 10 and height > 10:
                self.selected_region = (left, top, width, height)
                self.region_info_var.set(f"已选择区域: 左上角({left}, {top}), 大小 {width}x{height}")
                self.log(f"选择录制区域: 左上角({left}, {top}), 大小 {width}x{height}")
                selected = True
            else:
                messagebox.showinfo("提示", "请选择一个有效的区域")
            
            # 销毁选择窗口
            overlay.destroy()
            self.root.deiconify()  # 恢复主窗口
        
        def on_escape(event):
            # 按ESC键取消选择
            overlay.destroy()
            self.root.deiconify()  # 恢复主窗口
        
        # 绑定鼠标事件
        canvas.bind("<Button-1>", on_mouse_down)
        canvas.bind("<B1-Motion>", on_mouse_move)
        canvas.bind("<ButtonRelease-1>", on_mouse_up)
        
        # 绑定ESC键事件
        overlay.bind("<Escape>", on_escape)
        
        # 设置焦点确保ESC键生效
        overlay.focus_set()
        
        # 等待选择完成
        overlay.wait_window()
    
    def start_screen_preview(self):
        """开始录屏预览，根据选择的模式执行不同的预览逻辑"""
        if self.preview_manager.is_preview_active():
            self.preview_manager.stop_preview()
            self.screen_preview_button.config(text="开始预览")
            return
        
        mode = self.record_mode_var.get()
        fps = self.fps_var.get()
        
        if mode == "fullscreen":
            # 全屏预览
            screen_index = self.screen_index_var.get()
            if self.preview_manager.start_screen_preview(screen_index, 640, 480, fps):
                self.screen_preview_button.config(text="停止预览")
                self.status_var.set(f"预览中: 全屏录制 - 显示器{screen_index} - {fps}fps")
        
        elif mode == "region":
            # 区域预览
            if not self.selected_region:
                messagebox.showerror("错误", "请先选择录制区域")
                return
            
            left, top, width, height = self.selected_region
            # 计算预览窗口大小
            preview_width, preview_height = self._calculate_preview_size(width, height)
            if self.preview_manager.start_region_preview(left, top, preview_width, preview_height, width, height, fps):
                self.screen_preview_button.config(text="停止预览")
                self.status_var.set(f"预览中: 区域录制 - {width}x{height} - {fps}fps")
        
        elif mode == "application":
            # 应用预览
            selection = self.app_window_listbox.curselection()
            if not selection:
                messagebox.showerror("错误", "请选择一个应用窗口")
                return
            
            index = selection[0]
            window_info = self.window_manager.get_window_info(index)
            if not window_info:
                messagebox.showerror("错误", "无法获取窗口信息")
                return
            
            hwnd, title = window_info
            
            # 再次尝试将窗口置于前台，确保预览时窗口可见
            self.log(f"准备预览应用窗口: {title}")
            try:
                import win32gui
                # 先最小化再还原，确保窗口状态正常
                win32gui.ShowWindow(hwnd, 9)  # SW_RESTORE = 9
                # 激活窗口并设置为前台窗口
                win32gui.SetForegroundWindow(hwnd)
                # 短暂延迟确保窗口已置于前台
                import time
                time.sleep(0.3)
            except Exception as fw_error:
                self.log(f"将应用窗口 '{title}' 置于前台失败: {str(fw_error)}")
            
            # 获取音频设备
            audio_device = self.screen_audio_var.get()
            
            # 计算预览窗口大小
            preview_width, preview_height = 640, 480  # 默认预览尺寸
            if self.preview_manager.start_window_preview_dshow(title, preview_width, preview_height, audio_device=audio_device):
                self.screen_preview_button.config(text="停止预览")
                self.status_var.set(f"预览中: 应用录制(dshow) - {title} - {fps}fps")
    
    def start_screen_push(self):
        """开始录屏推流，根据选择的模式执行不同的推流逻辑"""
        if self.ffmpeg_pusher.is_streaming():
            self.stop_push()
            self.screen_push_button.config(text="开始推流")
            return
        
        # 停止预览
        if self.preview_manager.is_preview_active():
            self.preview_manager.stop_preview()
            self.screen_preview_button.config(text="开始预览")
        
        mode = self.record_mode_var.get()
        fps = self.fps_var.get()
        audio_device = self.screen_audio_var.get()
        
        if mode == "fullscreen":
            # 全屏录制
            screen_index = self.screen_index_var.get()
            if self.ffmpeg_pusher.start_screen_push(fps, audio_device, screen_index):
                self.screen_push_button.config(text="停止推流")
                self.status_var.set(f"推流中: 全屏录制 - 显示器{screen_index} - {fps}fps")
        
        elif mode == "region":
            # 区域录制
            if not self.selected_region:
                messagebox.showerror("错误", "请先选择录制区域")
                return
            
            left, top, width, height = self.selected_region
            if self.ffmpeg_pusher.start_region_push(left, top, width, height, fps, audio_device):
                self.screen_push_button.config(text="停止推流")
                self.status_var.set(f"推流中: 区域录制 - {width}x{height} - {fps}fps")
        
        elif mode == "application":
            # 应用录制
            selection = self.app_window_listbox.curselection()
            if not selection:
                messagebox.showerror("错误", "请选择一个应用窗口")
                return
            
            index = selection[0]
            window_info = self.window_manager.get_window_info(index)
            if not window_info:
                messagebox.showerror("错误", "无法获取窗口信息")
                return
            
            hwnd, title = window_info
            
            # 再次尝试将窗口置于前台，确保推流时窗口可见
            self.log(f"准备推流应用窗口: {title}")
            try:
                import win32gui
                # 先最小化再还原，确保窗口状态正常
                win32gui.ShowWindow(hwnd, 9)  # SW_RESTORE = 9
                # 激活窗口并设置为前台窗口
                win32gui.SetForegroundWindow(hwnd)
                # 短暂延迟确保窗口已置于前台
                import time
                time.sleep(0.3)
            except Exception as fw_error:
                self.log(f"将应用窗口 '{title}' 置于前台失败: {str(fw_error)}")
            
            # 使用dshow方式推流窗口
            if self.ffmpeg_pusher.start_window_push_dshow(title, audio_device):
                self.screen_push_button.config(text="停止推流")
                self.status_var.set(f"推流中: 应用录制(dshow) - {title} - {fps}fps")
    
    def stop_push(self):
        """停止推流"""
        # 停止音频监控
        self.stop_audio_monitoring()
        
        # 停止推流
        self.ffmpeg_pusher.stop_push()
        self.status_var.set("就绪")
        
        # 更新按钮状态
        self.device_push_button.config(text="开始推流")
        self.window_push_button.config(text="开始推流")
        self.screen_push_button.config(text="开始推流")
        
        # 重置音频电平显示
        if hasattr(self, 'audio_level_canvas') and hasattr(self, 'audio_level_bar'):
            self.audio_level = 0
            self.audio_level_canvas.coords(self.audio_level_bar, 0, 0, 0, 20)
        
        # 关闭迷你窗口
        if self.mini_window:
            self.mini_window.destroy()
            self.mini_window = None
    
    def _on_minimize(self, event):
        """处理窗口最小化事件"""
        # 检查是否是最小化事件且正在推流
        if self.ffmpeg_pusher.is_streaming() and not self.mini_window:
            # 获取当前推流状态作为标题
            status_text = self.status_var.get()
            # 创建迷你窗口
            self.create_mini_window(status_text)
    
    def create_mini_window(self, title):
        """创建推流状态迷你窗口，显示直播状态、时间和音频电平"""
        # 创建迷你窗口 - 更加专业的设计
        self.mini_window = tk.Toplevel(self.root)
        self.mini_window.title("直播中")
        self.mini_window.geometry("280x140")
        self.mini_window.resizable(False, False)
        self.mini_window.attributes("-topmost", True)  # 置顶
        
        # 设置窗口图标颜色
        self.mini_window.configure(bg="#2c3e50")
        
        # 顶部状态栏 - 显示直播状态
        status_frame = ttk.Frame(self.mini_window, height=30, style="StatusFrame.TFrame")
        status_frame.pack(fill=tk.X)
        
        # 直播指示灯
        indicator_canvas = tk.Canvas(status_frame, width=20, height=20, bg="#2c3e50", highlightthickness=0)
        indicator_canvas.pack(side=tk.LEFT, padx=10, pady=5)
        self.indicator_id = indicator_canvas.create_oval(2, 2, 18, 18, fill="#e74c3c")
        
        # 闪烁动画
        self._animate_indicator(indicator_canvas)
        
        # 直播标签
        ttk.Label(status_frame, text="直播中", font=("微软雅黑", 11, "bold"), 
                 foreground="white", background="#2c3e50").pack(side=tk.LEFT, padx=5)
        
        # 中间内容区域
        content_frame = ttk.Frame(self.mini_window, padding=10)
        content_frame.pack(fill=tk.BOTH, expand=True)
        
        # 推流标题
        self.mini_title_var = tk.StringVar(value=f"{title}")
        title_label = ttk.Label(content_frame, textvariable=self.mini_title_var, font=('微软雅黑', 10), 
                              foreground="white", wraplength=240)
        title_label.pack(pady=(0, 8), anchor=tk.W)
        
        # 时间和音频电平显示框架
        info_frame = ttk.Frame(content_frame)
        info_frame.pack(fill=tk.X, pady=5)
        
        # 推流时间
        self.time_var = tk.StringVar(value="00:00:00")
        time_label = ttk.Label(info_frame, text="时间:", font=('微软雅黑', 9), 
                             foreground="white")
        time_label.pack(side=tk.LEFT, padx=(0, 5))
        
        time_value_label = ttk.Label(info_frame, textvariable=self.time_var, font=("微软雅黑", 12, "bold"), 
                                   foreground="#3498db")
        time_value_label.pack(side=tk.LEFT, padx=5)
        
        # 底部控制按钮
        button_frame = ttk.Frame(self.mini_window, padding=5)
        button_frame.pack(fill=tk.X)
        
        # 显示主窗口按钮（居中，只有一个返回按钮）
        ttk.Button(button_frame, text="返回", command=self.show_main_window, width=20).pack(pady=10)
        
        # 绑定关闭事件
        self.mini_window.protocol("WM_DELETE_WINDOW", self.show_main_window)
        
        # 启动音频电平监控
        self.start_audio_monitoring()
        
        # 启动计时器
        self.update_timer()
    
    def _animate_indicator(self, canvas):
        """直播指示灯闪烁动画"""
        if not hasattr(self, '_indicator_state'):
            self._indicator_state = True
        
        if hasattr(self, 'mini_window') and self.mini_window:
            # 切换颜色实现闪烁效果
            color = "#e74c3c" if self._indicator_state else "#c0392b"
            canvas.itemconfig(self.indicator_id, fill=color)
            self._indicator_state = not self._indicator_state
            
            # 继续动画
            canvas.after(500, lambda: self._animate_indicator(canvas))
    
    def start_audio_monitoring(self):
        """开始音频电平监控"""
        self.audio_monitoring = True
        self.audio_thread = threading.Thread(target=self._monitor_audio_level, daemon=True)
        self.audio_thread.start()
    
    def _monitor_audio_level(self):
        """音频电平监控线程函数"""
        # 初始化音频电平
        self.audio_level = 0
        
        # 模拟音频电平变化，实际应用中可以从音频设备读取
        while self.audio_monitoring:
            try:
                # 生成随机音频电平 (0-100)，添加一些变化规律使其更自然
                # 基础值在0-30之间，偶尔出现较大值
                if np.random.random() < 0.1:  # 10%概率出现较大音量
                    self.audio_level = np.random.randint(40, 90)
                else:
                    # 小音量状态下的细微波动
                    self.audio_level = max(0, min(30, self.audio_level + np.random.randint(-5, 6)))
                
                # 更新主界面的音频电平指示器
                if hasattr(self, 'audio_level_canvas') and hasattr(self, 'audio_level_bar'):
                    self.root.after(0, self._update_audio_level_display)
                
                # 更新迷你窗口的音频电平指示器
                if hasattr(self, 'mini_window') and self.mini_window and hasattr(self, 'mini_audio_canvas') and hasattr(self, 'mini_audio_bar'):
                    self.mini_window.after(0, self._update_mini_audio_level)
                
                time.sleep(0.1)  # 100ms更新一次
            except Exception as e:
                self.log(f"音频监控错误: {str(e)}")
                break
    
    def _update_audio_level_display(self):
        """更新主界面音频电平显示"""
        if hasattr(self, 'audio_level_canvas') and hasattr(self, 'audio_level_bar'):
            # 显示电平条，隐藏提示文字
            self.audio_level_canvas.itemconfigure(self.audio_level_bar, state="normal")
            if hasattr(self, 'audio_level_text'):
                self.audio_level_canvas.itemconfigure(self.audio_level_text, state="hidden")
            
            # 根据音频电平计算宽度
            bar_width = int(self.audio_level * 0.8)  # 最大80像素
            
            # 更新音频条宽度
            self.audio_level_canvas.coords(self.audio_level_bar, 0, 0, bar_width, 20)
            
            # 根据音频电平改变颜色
            if self.audio_level < 30:
                color = "#4CAF50"  # 绿色
            elif self.audio_level < 70:
                color = "#FFC107"  # 黄色
            else:
                color = "#F44336"  # 红色
            
            self.audio_level_canvas.itemconfig(self.audio_level_bar, fill=color)
    
    def _update_mini_audio_level(self):
        """更新迷你窗口音频电平显示"""
        if hasattr(self, 'mini_audio_canvas') and hasattr(self, 'mini_audio_bar'):
            # 根据音频电平计算宽度
            bar_width = int(self.audio_level * 0.4)  # 最大40像素
            
            # 更新音频条宽度
            self.mini_audio_canvas.coords(self.mini_audio_bar, 0, 0, bar_width, 10)
            
            # 根据音频电平改变颜色
            if self.audio_level < 30:
                color = "#4CAF50"  # 绿色
            elif self.audio_level < 70:
                color = "#FFC107"  # 黄色
            else:
                color = "#F44336"  # 红色
            
            self.mini_audio_canvas.itemconfig(self.mini_audio_bar, fill=color)
    
    def stop_audio_monitoring(self):
        """停止音频电平监控"""
        self.audio_monitoring = False
        if hasattr(self, 'audio_thread'):
            try:
                # 不需要join，因为线程是daemon的
                pass
            except:
                pass
            self.audio_thread = None
        
        # 恢复显示提示文字，隐藏电平条
        if hasattr(self, 'audio_level_canvas'):
            if hasattr(self, 'audio_level_text'):
                self.audio_level_canvas.itemconfigure(self.audio_level_text, state="normal")
            if hasattr(self, 'audio_level_bar'):
                self.audio_level_canvas.itemconfigure(self.audio_level_bar, state="hidden")
        
        # 恢复显示提示文字，隐藏电平条
        if hasattr(self, 'audio_level_canvas'):
            if hasattr(self, 'audio_level_text'):
                self.audio_level_canvas.itemconfigure(self.audio_level_text, state="normal")
            if hasattr(self, 'audio_level_bar'):
                self.audio_level_canvas.itemconfigure(self.audio_level_bar, state="hidden")
    
    def update_timer(self):
        """更新计时器显示"""
        if self.ffmpeg_pusher.is_streaming() and self.mini_window:
            elapsed_time = self.ffmpeg_pusher.get_elapsed_time()
            hours = elapsed_time // 3600
            minutes = (elapsed_time % 3600) // 60
            seconds = elapsed_time % 60
            self.time_var.set(f"{hours:02d}:{minutes:02d}:{seconds:02d}")
            self.mini_window.after(1000, self.update_timer)
    
    def show_main_window(self):
        """显示主窗口（不停止推流）"""
        # 停止音频监控
        self.stop_audio_monitoring()
        
        # 销毁迷你窗口
        if hasattr(self, 'mini_window') and self.mini_window:
            self.mini_window.destroy()
            self.mini_window = None
        
        # 显示主窗口
        self.root.deiconify()
    
    def show_help(self):
        """显示使用帮助"""
        help_window = tk.Toplevel(self.root)
        help_window.title("使用帮助")
        help_window.geometry("600x400")
        help_window.transient(self.root)
        
        # 创建文本区域显示帮助内容
        text_widget = tk.Text(help_window, wrap=tk.WORD, font=("微软雅黑", 10))
        text_widget.pack(fill=tk.BOTH, expand=True, padx=20, pady=20)
        
        # 添加滚动条
        scrollbar = ttk.Scrollbar(text_widget, command=text_widget.yview)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        text_widget.config(yscrollcommand=scrollbar.set)
        
        # 帮助内容
        help_content = """
PC推流工具使用指南

一、基本功能
本工具支持四种推流模式：摄像头推流、应用窗口推流、录屏推流和本地文件推流。

二、使用步骤
1. 设备推流：
   - 选择视频设备和音频设备
   - 点击「开始预览」查看效果
   - 点击「开始推流」开始直播

2. 窗口推流：
   - 点击「刷新窗口列表」
   - 选择要推流的窗口
   - 选择音频设备
   - 点击「开始预览」或「开始推流」

3. 录屏推流：
   - 选择录制模式（全屏、区域或应用）
   - 根据选择的模式进行相应设置
   - 设置帧率和音频设备
   - 点击「开始预览」或「开始推流」

4. 本地文件推流：
   - 点击「浏览...」选择本地视频文件
   - 查看文件信息（时长、分辨率、编码等）
   - 根据需要调整推流参数（视频/音频编码和码率）
   - 点击「预览」查看效果
   - 点击「开始推流」进行直播推送

三、快捷键
- F5: 刷新设备列表
- Ctrl+S: 打开配置
- Alt+F4: 退出程序

四、注意事项
- 确保已正确配置FFmpeg路径和RTMP地址
- 推流过程中可以点击主界面按钮返回主界面
- 区域选择时可使用ESC键取消选择
- 本地文件推流支持常见视频格式（MP4、AVI、MKV等）
- 文件预览窗口可按ESC键退出
        """
        
        text_widget.insert(tk.END, help_content)
        text_widget.config(state=tk.DISABLED)  # 只读
    
    def show_about(self):
        """显示关于信息"""
        messagebox.showinfo(
            "关于", 
            "PC推流工具 v1.0\n\n" +
            "一个基于Python和FFmpeg的简单可靠的RTMP推流工具\n" +
            "支持摄像头推流、应用窗口推流、录屏推流和本地文件推流"
        )
    
    def on_closing(self):
        """处理关闭事件"""
        # 停止预览
        if self.preview_manager.is_preview_active():
            self.preview_manager.stop_preview()
        
        # 停止音频监控
        self.stop_audio_monitoring()
        
        if self.ffmpeg_pusher.is_streaming():
            if messagebox.askyesno("确认退出", "正在推流中，确定要退出吗？"):
                self.stop_push()
                self.root.destroy()
        else:
            self.root.destroy()
    
    def browse_file(self):
        """浏览并选择本地视频文件"""
        from tkinter import filedialog
        file_types = [
            ("视频文件", "*.mp4 *.avi *.mkv *.mov *.flv *.wmv *.webm"),
            ("所有文件", "*.*")
        ]
        file_path = filedialog.askopenfilename(title="选择视频文件", filetypes=file_types)
        if file_path:
            self.file_path_var.set(file_path)
            # 尝试获取文件信息
            self._get_file_info(file_path)
    
    def _get_file_info(self, file_path):
        """获取视频文件信息"""
        try:
            # 使用FFprobe获取文件信息
            import subprocess
            import json
            ffprobe_path = self.config_manager.get_config("ffmpeg_path")
            # 将ffmpeg替换为ffprobe（假设在同一目录下）
            ffprobe_path = ffprobe_path.replace("ffmpeg.exe", "ffprobe.exe")
            
            cmd = [
                ffprobe_path,
                "-v", "quiet",
                "-print_format", "json",
                "-show_format",
                "-show_streams",
                file_path
            ]
            result = subprocess.run(cmd, capture_output=True, text=True, timeout=10)
            
            if result.returncode == 0:
                info = json.loads(result.stdout)
                # 提取基本信息
                format_info = info.get("format", {})
                duration = float(format_info.get("duration", 0))
                size = float(format_info.get("size", 0)) / (1024 * 1024)  # 转换为MB
                
                # 提取视频流信息
                video_stream = next((s for s in info.get("streams", []) if s.get("codec_type") == "video"), None)
                video_info = ""
                if video_stream:
                    width = video_stream.get("width", 0)
                    height = video_stream.get("height", 0)
                    fps = eval(video_stream.get("r_frame_rate", "0/1"))
                    codec_name = video_stream.get("codec_name", "未知")
                    video_info = f"分辨率: {width}x{height}, 帧率: {fps:.2f}fps, 编码: {codec_name}"
                
                # 提取音频流信息
                audio_stream = next((s for s in info.get("streams", []) if s.get("codec_type") == "audio"), None)
                audio_info = ""
                if audio_stream:
                    channels = audio_stream.get("channels", 0)
                    sample_rate = audio_stream.get("sample_rate", 0)
                    codec_name = audio_stream.get("codec_name", "未知")
                    audio_info = f"声道: {channels}, 采样率: {sample_rate}Hz, 编码: {codec_name}"
                
                # 格式化文件信息
                hours, remainder = divmod(int(duration), 3600)
                minutes, seconds = divmod(remainder, 60)
                duration_str = f"{hours:02d}:{minutes:02d}:{seconds:02d}"
                
                file_info = f"文件路径: {file_path}\n"\
                            f"时长: {duration_str}\n"\
                            f"文件大小: {size:.2f} MB\n"\
                            f"{video_info}\n"\
                            f"{audio_info}"
                
                self.file_info_var.set(file_info)
            else:
                self.file_info_var.set(f"无法获取文件信息: {result.stderr}")
                self.log(f"FFprobe错误: {result.stderr}")
        except Exception as e:
            self.file_info_var.set(f"获取文件信息失败: {str(e)}")
            self.log(f"获取文件信息异常: {str(e)}")
    
    def preview_file(self):
        """预览本地视频文件"""
        file_path = self.file_path_var.get()
        if not file_path:
            messagebox.showwarning("警告", "请先选择一个视频文件")
            return
        
        try:
            # 停止当前预览
            if self.preview_manager.is_preview_active():
                self.preview_manager.stop_preview()
            
            # 开始文件预览
            self.log("开始预览视频文件...")
            self.preview_manager.preview_file(file_path)
        except Exception as e:
            self.log(f"预览失败: {str(e)}")
            messagebox.showerror("错误", f"预览失败: {str(e)}")
    
    def start_file_push(self):
        """开始本地文件推流"""
        file_path = self.file_path_var.get()
        if not file_path:
            messagebox.showwarning("警告", "请先选择一个视频文件")
            return
        
        # 检查配置
        if not self._check_config():
            return
        
        try:
            # 停止预览
            if self.preview_manager.is_preview_active():
                self.preview_manager.stop_preview()
            
            # 获取推流参数
            video_codec = self.video_codec_var.get()
            video_bitrate = f"{self.video_bitrate_var.get()}k"
            audio_codec = self.audio_codec_var.get()
            audio_bitrate = f"{self.audio_bitrate_var.get()}k"
            
            # 构建推流命令
            rtmp_url = self.config_manager.get_config("rtmp_url")
            
            # 构建ffmpeg命令参数
            push_command = [
                "-re",  # 按照视频原始帧率读取
                "-i", file_path,
                "-c:v", video_codec,
                "-b:v", video_bitrate,
                "-preset", "veryfast",  # 使用veryfast预设以保证性能
                "-c:a", audio_codec,
                "-b:a", audio_bitrate,
                "-f", "flv",  # 输出格式为FLV
                rtmp_url
            ]
            
            # 开始推流
            self.log("开始推流本地文件...")
            self.log(f"推流参数: 视频编码={video_codec}, 视频码率={video_bitrate}, 音频编码={audio_codec}, 音频码率={audio_bitrate}")
            
            # 启动推流
            if self.ffmpeg_pusher.start_push(push_command):
                self.log("推流启动成功")
                # 更新按钮状态
                self.file_push_btn.config(state="disabled")
                self.file_stop_btn.config(state="normal")
                self.file_preview_btn.config(state="disabled")
                # 启动音频监控
                self.start_audio_monitoring()
                # 切换到迷你窗口
                self.show_mini_window()
            else:
                self.log("推流启动失败")
        except Exception as e:
            self.log(f"推流异常: {str(e)}")
            messagebox.showerror("错误", f"推流失败: {str(e)}")
    
    def stop_file_push(self):
        """停止本地文件推流"""
        try:
            self.log("停止推流...")
            self.ffmpeg_pusher.stop_push()
            self.log("推流已停止")
            
            # 恢复按钮状态
            self.file_push_btn.config(state="normal")
            self.file_stop_btn.config(state="disabled")
            self.file_preview_btn.config(state="normal")
            
            # 停止音频监控
            self.stop_audio_monitoring()
            
            # 恢复主窗口显示
            if self.mini_window:
                self.restore_main_window()
        except Exception as e:
            self.log(f"停止推流异常: {str(e)}")
            messagebox.showerror("错误", f"停止推流失败: {str(e)}")